/*
 * Copyright (C) 2013 Manoel Afonso Filho
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package agimagem.concrete;

import agimagem.populacao.Populacao;
import agimagem.strategy.Inicializacao;
import agimagem.strategy.Selecao;
import agimagem.util.Util;

/**
 * Implementação da Seleção por Torneio.
 * @author Manoel Afonso Filho
 */
public class TorneioBinario implements Selecao {

    /**
     * Total de indivíduos a serem selecionados.
     */
    private int totalIndividuos;

    /**
     * Proporção (em %) do total de indivíduos a serem selecionados da populão.
     */
    private double proporcao = 0.4;

    /**
     * Cria o torneio, calculando o total de indivíduos a serem retornados.
     */
    public TorneioBinario(){
        this.totalIndividuos = (int) (Inicializacao.numIndividuos * proporcao);
        System.out.println(totalIndividuos);
    }

    /**
     * Seleciona x membros da população atual para o Crossover e Mutação.
     * São escolhidos dois indivíduos, aleatoriamente, da população. Aquele
     * que possuir maior fitness entra no mating pool.
     * @param pop População atual.
     * @return Indivíduos selecionados.
     */
    @Override
    public Populacao executar(Populacao pop) {
        Populacao matingPool = new Populacao();
        for (int i = 0; i < this.totalIndividuos; i++) {
            // Seleciona *dois* indivíduos aleatoriamente. Seleção por torneio binária (k=2).
            // Isso permite que os melhores indivíduos sejam sorteados mais frequentemente,
            // mas não tanto a ponto de sortear sempre os mesmos melhores indivíduos.
            int id1 = (int)(Util.sr.nextDouble() * (pop.getTamanhoPop() - 1));
            int id2 = (int)(Util.sr.nextDouble() * (pop.getTamanhoPop() - 1));

            double fitness1 = pop.getFitnessIndividuoAt(id1);
            double fitness2 = pop.getFitnessIndividuoAt(id2);

            // Só fazemos a clonagem quando vamos adicionar ao matingPool.
            if(fitness1 >= fitness2){
                matingPool.guardarIndividuo(pop.getIndividuoAt(id1));
            } else{
                matingPool.guardarIndividuo(pop.getIndividuoAt(id2));
            }
        }
        return matingPool;
    }

    /**
     * @return the tamanhoTorneio
     */
    public int getTotalIndividuos() {
        return totalIndividuos;
    }

    /**
     * @param tamanhoTorneio O tamanho do torneio. Quanto maior, maiores são
     * as chances de indivíduos com fitness pequeno serem selecionados.
     */
    public void setTotalIndividuos(int tamanhoTorneio) {
        if(tamanhoTorneio == 0){
            tamanhoTorneio = 2;
        } else if (tamanhoTorneio % 2 != 0){
            tamanhoTorneio += 1;
        }

        this.totalIndividuos = tamanhoTorneio;
    }

}
